using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Utilities;

namespace SharePoint.DataMapper
{
	/// <summary>
	/// Helper classes for <c>SharePoint</c> object model.
	/// </summary>
	public static class Service
	{
		/// <summary>
		/// Gets the list GUID.
		/// </summary>
		/// <param name="web"><see cref="SPWeb"/> object. Can be null.</param>
		/// <param name="listNameOrIdOrUrl">The list Name, Id or Url.</param>
		/// <returns><see cref="Guid"/> of the list. <see cref="Guid.Empty"/> if list not found.</returns>
		public static Guid GetListGuid(this SPWeb web, string listNameOrIdOrUrl)
		{
			SPList list;
			try
			{
				if (web == null)
				{
					return new Guid(listNameOrIdOrUrl);
				}
				try
				{
					Guid guid = new Guid(listNameOrIdOrUrl);
					list = web.Lists[guid];
				}
				catch (FormatException)
				{
					try
					{
						list = web.Lists[listNameOrIdOrUrl];
					}
					catch (Exception)
					{
						list = web.GetList(SPUrlUtility.CombineUrl(web.ServerRelativeUrl, listNameOrIdOrUrl));
					}
				}
				if (list != null)
					return list.ID;
				return Guid.Empty;
			}
			catch (Exception)
			{
				return Guid.Empty;
			}
		}

		/// <summary>
		/// Gets the list collection of <see cref="SPWeb"/>.
		/// </summary>
		/// <param name="web"><see cref="SPWeb"/> object.</param>
		/// <returns></returns>
		public static IEnumerable<SPList> GetListCollection(this SPWeb web)
		{
			return GetListCollection(web, list => !list.Hidden && list.Fields.Count > 0);
		}

		/// <summary>
		/// Gets the list collection of <see cref="SPWeb"/> with <see cref="SPBaseType"/>.
		/// </summary>
		/// <param name="web"><see cref="SPWeb"/> object.</param>
		/// <param name="type"><see cref="SPBaseType"/> object.</param>
		/// <returns></returns>
		/// <exception cref="ArgumentNullException"><c>ArgumentNullException</c> if web is null.</exception>
		public static IEnumerable<SPList> GetListCollection(this SPWeb web, SPBaseType type)
		{
			return GetListCollection(web, list => !list.Hidden && list.Fields.Count > 0 && list.BaseType == type);
		}

		/// <summary>
		/// Gets the list collection.
		/// </summary>
		/// <param name="web">The web.</param>
		/// <param name="templateType">Type of the template.</param>
		/// <returns></returns>
		public static IEnumerable<SPList> GetListCollection(this SPWeb web, SPListTemplateType templateType)
		{
			return GetListCollection(web, list => list.BaseTemplate == templateType);
		}

		/// <summary>
		/// Gets the collection of lists, with SPContentType is child of contentTypeId.
		/// </summary>
		/// <param name="web">The web.</param>
		/// <param name="contentTypeId">The content type id.</param>
		/// <returns></returns>
		public static IEnumerable<SPList> GetListCollection(this SPWeb web, SPContentTypeId contentTypeId)
		{
			return GetListCollection(web, delegate(SPList list)
			                              	{
			                              		foreach (SPContentType type in list.ContentTypes)
			                              		{
			                              			if (type.Id.IsChildOf(contentTypeId))
			                              			{
			                              				return true;
			                              			}
			                              		}
			                              		return false;
			                              	});
		}

		/// <summary>
		/// Gets the collection of SPList in SPWeb, matching delegate.
		/// </summary>
		/// <param name="web">The web.</param>
		/// <param name="match">The match.</param>
		/// <returns></returns>
		public static IEnumerable<SPList> GetListCollection(this SPWeb web, Predicate<SPList> match)
		{
			if (web == null)
			{
				throw new ArgumentNullException("web");
			}
			return from SPList list in web.Lists where match(list) select list;
		}

		/// <summary>
		/// Gets the View.
		/// </summary>
		/// <param name="list"></param>
		/// <param name="viewNameOrId"></param>
		/// <returns></returns>
		public static SPView GetView(this SPList list, string viewNameOrId)
		{
			if (!string.IsNullOrEmpty(viewNameOrId))
			{
				try
				{
					Guid guid = new Guid(viewNameOrId);
					return list.Views[guid];
				}
				catch (ArgumentOutOfRangeException)
				{
				}
				catch (ArgumentException)
				{
				}
				catch (FormatException)
				{
					try
					{
						return list.Views[viewNameOrId];
					}
					catch (ArgumentOutOfRangeException)
					{
					}
				}
			}
			return list.DefaultView;
		}

		/// <summary>
		/// Gets the field.
		/// </summary>
		/// <param name="web">The web.</param>
		/// <param name="listNameOrId">The list name or id.</param>
		/// <param name="fieldName">Name of the field.</param>
		/// <returns></returns>
		public static SPField GetField(this SPWeb web, string listNameOrId, string fieldName)
		{
			Guid listId = GetListGuid(web, listNameOrId);
			if (listId != Guid.Empty)
			{
				SPList list = web.Lists[listId];
				return GetField(list, fieldName);
			}
			return null;
		}

		/// <summary>
		/// Gets the field.
		/// </summary>
		/// <param name="list">The list.</param>
		/// <param name="fieldName">Name of the field.</param>
		/// <returns></returns>
		public static SPField GetField(this SPList list, string fieldName)
		{
			return GetField(list.Fields, fieldName);
		}


		/// <summary>
		/// Gets the field.
		/// </summary>
		/// <param name="contentType">Type of the content.</param>
		/// <param name="fieldName">Name of the field.</param>
		/// <returns></returns>
		public static SPField GetField(this SPContentType contentType, string fieldName)
		{
			return GetField(contentType.Fields, fieldName);
		}

		/// <summary>
		/// Gets the field.
		/// </summary>
		/// <param name="web">SPWeb.</param>
		/// <param name="fieldName">Name of the field.</param>
		/// <returns></returns>
		public static SPField GetField(this SPWeb web, string fieldName)
		{
			return GetField(web.Fields, fieldName);
		}

		/// <summary>
		/// Gets the field.
		/// </summary>
		/// <param name="fieldCollection">The field collection.</param>
		/// <param name="fieldNameOrId">Name or Id of the field.</param>
		/// <returns></returns>
		public static SPField GetField(SPFieldCollection fieldCollection, string fieldNameOrId)
		{
			if (fieldCollection.ContainsField(fieldNameOrId))
			{
				return fieldCollection.GetField(fieldNameOrId);
			}
			try
			{
				Guid guid = new Guid(fieldNameOrId);
				return fieldCollection[guid];
			}
			catch (FormatException)
			{
				return null;
			}
			catch (ArgumentException)
			{
				return null;
			}
		}

		/// <summary>
		/// Gets the field collection.
		/// </summary>
		/// <param name="web">The web.</param>
		/// <param name="listNameOrId">The list name or id.</param>
		/// <returns></returns>
		public static IEnumerable<SPField> GetFieldCollection(SPWeb web, string listNameOrId)
		{
			return GetFieldCollection(web, listNameOrId, SPFieldType.Invalid, false);
		}

		/// <summary>
		/// Gets the field collection.
		/// </summary>
		/// <param name="web">The web.</param>
		/// <param name="listNameOrId">The list name or id.</param>
		/// <param name="type">The type.</param>
		/// <returns></returns>
		public static IEnumerable<SPField> GetFieldCollection(SPWeb web, string listNameOrId, SPFieldType type)
		{
			return GetFieldCollection(web, listNameOrId, type, false);
		}

		/// <summary>
		/// Gets the field collection.
		/// </summary>
		/// <param name="web">The web.</param>
		/// <param name="listNameOrId">The list name or id.</param>
		/// <param name="type">The type.</param>
		/// <param name="isWritable">if set to <c>true</c> returns only writable Fields.</param>
		/// <returns></returns>
		public static IEnumerable<SPField> GetFieldCollection(SPWeb web, string listNameOrId, SPFieldType type, bool isWritable)
		{
			Guid listId = GetListGuid(web, listNameOrId);
			if (listId != Guid.Empty)
			{
				return GetFieldCollection(web.Lists[listId], type, isWritable);
			}
			return null;
		}

		/// <summary>
		/// Gets the field collection.
		/// </summary>
		/// <param name="list">The list.</param>
		/// <returns></returns>
		public static IEnumerable<SPField> GetFieldCollection(this SPList list)
		{
			return GetFieldCollection(list, field => true);
		}

		/// <summary>
		/// Gets the field collection.
		/// </summary>
		/// <param name="list">The list.</param>
		/// <param name="type">The type.</param>
		/// <param name="isWritable">if set to <c>true</c> returns only writable Fields.</param>
		/// <returns></returns>
		public static IEnumerable<SPField> GetFieldCollection(this SPList list, SPFieldType type, bool isWritable)
		{
			return GetFieldCollection(list, field => !field.Hidden && field.Type == type && !(isWritable && field.ReadOnlyField));
		}

		/// <summary>
		/// Gets the field collection.
		/// </summary>
		/// <param name="list">The list.</param>
		/// <param name="match">The match.</param>
		/// <returns><see cref="SPField"/> collection.</returns>
		/// <exception cref="ArgumentNullException"><c>ArgumentNullException</c> if list is null.</exception>
		public static IEnumerable<SPField> GetFieldCollection(this SPList list, Predicate<SPField> match)
		{
			if (list == null)
			{
				throw new ArgumentNullException("list");
			}
			return from SPField field in list.Fields
			       where match(field)
			       select field;
		}

		/// <summary>
		/// Gets the site.
		/// </summary>
		/// <param name="siteUrlOrId">The site URL or id.</param>
		/// <returns></returns>
		public static SPSite GetSite(string siteUrlOrId)
		{
			try
			{
				try
				{
					Guid siteId = new Guid(siteUrlOrId);
					if (siteId != Guid.Empty)
					{
						return new SPSite(siteId);
					}
				}
				catch (FormatException)
				{
					return new SPSite(siteUrlOrId);
				}

			}
			catch (FileNotFoundException)
			{
				return null;
			}
			return null;
		}

		/// <summary>
		/// Gets the web.
		/// </summary>
		/// <param name="site">The site.</param>
		/// <param name="webUrlOrId">The web URL or id.</param>
		/// <returns></returns>
		public static SPWeb GetWeb(this SPSite site, string webUrlOrId)
		{
			if (null != site)
			{
				if (string.IsNullOrEmpty(webUrlOrId))
				{
					return site.OpenWeb();
				}

				try
				{
					Guid webId = new Guid(webUrlOrId);
					if (webId != Guid.Empty)
					{
						return site.OpenWeb(webId);
					}
				}
				catch (FormatException)
				{
					try
					{
						return site.OpenWeb(webUrlOrId);
					}
					catch (ArgumentException)
					{
						return site.OpenWeb();
					}
				}
				return site.OpenWeb();
			}
			return null;
		}

		/// <summary>
		/// Gets the list.
		/// </summary>
		/// <param name="web">The web.</param>
		/// <param name="listNameOrIdOrUrl">The list Name, Id or Url.</param>
		/// <returns></returns>
		public static SPList GetList(SPWeb web, string listNameOrIdOrUrl)
		{
			if (web == null)
			{
				throw new ArgumentNullException("web");
			}
			if (listNameOrIdOrUrl == "UserInfo")
			{
				return web.SiteUserInfoList;
			}
			Guid listId = GetListGuid(web, listNameOrIdOrUrl);
			if (listId != Guid.Empty)
			{
				return web.Lists[listId];
			}
			return null;
		}

		/// <summary>
		/// Gets the type of the content.
		/// </summary>
		/// <param name="web">The web.</param>
		/// <param name="contentTypeId">The content type id.</param>
		/// <returns></returns>
		public static SPContentType GetContentType(this SPWeb web, string contentTypeId)
		{
			return GetContentType(web.AvailableContentTypes, contentTypeId);
		}

		/// <summary>
		/// Gets the type of the content.
		/// </summary>
		/// <param name="list">The list.</param>
		/// <param name="contentTypeId">The content type id.</param>
		/// <returns></returns>
		public static SPContentType GetContentType(this SPList list, string contentTypeId)
		{
			return GetContentType(list.ContentTypes, contentTypeId);
		}

		/// <summary>
		/// Gets the type of the content.
		/// </summary>
		/// <param name="collection">The collection.</param>
		/// <param name="contentTypeId">The content type id.</param>
		/// <returns></returns>
		public static SPContentType GetContentType(SPContentTypeCollection collection, string contentTypeId)
		{
			SPContentType contentType;
			try
			{
				contentType = collection[new SPContentTypeId(contentTypeId)];
			}
			catch
			{
				contentType = null;
			}
			return contentType;
		}

		/// <summary>
		/// Gets the content type collection.
		/// </summary>
		/// <param name="web">The web.</param>
		/// <param name="baseContentTypeId">The base content type id.</param>
		/// <returns></returns>
		public static IEnumerable<SPContentType> GetContentTypeCollection(this SPWeb web, string baseContentTypeId)
		{
			return GetContentTypeCollection(web.AvailableContentTypes, baseContentTypeId);
		}

		/// <summary>
		/// Gets the content type collection.
		/// </summary>
		/// <param name="list">The list.</param>
		/// <param name="baseContentTypeId">The base content type id.</param>
		/// <returns></returns>
		public static IEnumerable<SPContentType> GetContentTypeCollection(this SPList list, string baseContentTypeId)
		{
			return GetContentTypeCollection(list.ContentTypes, baseContentTypeId);
		}

		/// <summary>
		/// Gets the content type collection.
		/// </summary>
		/// <param name="collection">The collection.</param>
		/// <param name="baseContentTypeId">The base content type id.</param>
		/// <returns></returns>
		public static IEnumerable<SPContentType> GetContentTypeCollection(SPContentTypeCollection collection, string baseContentTypeId)
		{
			return GetContentTypeCollection(collection, contentType => contentType.Id.IsChildOf(new SPContentTypeId(baseContentTypeId)));
		}

		/// <summary>
		/// Gets the content type collection.
		/// </summary>
		/// <param name="collection">The collection.</param>
		/// <param name="match">The match.</param>
		/// <returns></returns>
		public static IEnumerable<SPContentType> GetContentTypeCollection(SPContentTypeCollection collection, Predicate<SPContentType> match)
		{
			foreach (SPContentType contentType in collection)
			{
				if (match(contentType))
				{
					yield return contentType;
				}
			}
		}
	}
}